package Leetcode.Dichotomy;

import java.util.Arrays;

/**
 * @Author: kirito
 * @Date: 2024/4/7 14:12
 * @Description:
 * 袋子里最少数目的球
 * 中等
 * 相关标签
 * 相关企业
 * 提示
 * 给你一个整数数组 nums ，其中 nums[i] 表示第 i 个袋子里球的数目。同时给你一个整数 maxOperations 。
 *
 * 你可以进行如下操作至多 maxOperations 次：
 *
 * 选择任意一个袋子，并将袋子里的球分到 2 个新的袋子中，每个袋子里都有 正整数 个球。
 * 比方说，一个袋子里有 5 个球，你可以把它们分到两个新袋子里，分别有 1 个和 4 个球，或者分别有 2 个和 3 个球。
 * 你的开销是单个袋子里球数目的 最大值 ，你想要 最小化 开销。
 *
 * 请你返回进行上述操作后的最小开销。
 *
 *
 *
 * 示例 1：
 *
 * 输入：nums = [9], maxOperations = 2
 * 输出：3
 * 解释：
 * - 将装有 9 个球的袋子分成装有 6 个和 3 个球的袋子。[9] -> [6,3] 。
 * - 将装有 6 个球的袋子分成装有 3 个和 3 个球的袋子。[6,3] -> [3,3,3] 。
 * 装有最多球的袋子里装有 3 个球，所以开销为 3 并返回 3 。
 * 示例 2：
 *
 * 输入：nums = [2,4,8,2], maxOperations = 4
 * 输出：2
 * 解释：
 * - 将装有 8 个球的袋子分成装有 4 个和 4 个球的袋子。[2,4,8,2] -> [2,4,4,4,2] 。
 * - 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,4,4,4,2] -> [2,2,2,4,4,2] 。
 * - 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,2,2,4,4,2] -> [2,2,2,2,2,4,2] 。
 * - 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,2,2,2,2,4,2] -> [2,2,2,2,2,2,2,2] 。
 * 装有最多球的袋子里装有 2 个球，所以开销为 2 并返回 2 。
 * 示例 3：
 *
 * 输入：nums = [7,17], maxOperations = 2
 * 输出：7
 *
 *
 * 提示：
 *
 * 1 <= nums.length <= 105
 * 1 <= maxOperations, nums[i] <= 109
 */

public class minimumSize {
    public static void main(String[] args) {
        int[] nums = {7,17};
        int maxOp = 2;
        System.out.println(minimumSize(nums, maxOp));

    }
    /**
     * 每次只能从一个袋子里边拿球出来   所以操作数每次+1
     * 然后要想尽可能最小化开销，既每个袋子都平均分配球的个数，且总操作数小于maxOperations
     * 左边界就是1咯，右边界的话应该也是max（数组）
     * @param nums
     * @param maxOperations
     * @return
     */
    public static int minimumSize(int[] nums, int maxOperations) {
        int left = 1, right = Arrays.stream(nums).max().getAsInt();
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (!check(nums, maxOperations, mid)) {
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return left;
    }

    /**
     * 每次只能从一个袋子里边拿球出来   所以操作数每次+1
     * 但是需要考虑拿出来的多余的球放到哪些口袋里去呢，又是否能够放的下呢？
     *
     * 选择任意一个袋子，并将袋子里的球分到 2 个新的袋子中，每个袋子里都有 正整数 个球。
     * 那就上面的逻辑不对，得改一下
     *
     *
     * @param nums
     * @param maxOperations
     * @param mid
     * @return
     */
    private static boolean check(int[] nums, int maxOperations, int mid) {
        int operationCount = 0;
        for (int num : nums) {
            /* 这里用除法。。。。。。当时有点哈皮了，我干嘛一个个减呢
            while (num > mid) {
                num -= mid;
                operationCount++;
                if (operationCount > maxOperations) {
                    return false;
                }
            }*/
            operationCount += (num - 1) / mid;
            if (operationCount > maxOperations) {
                return false;
            }
        }
        //总操作数小于题目给的操作数
        return operationCount <= maxOperations;
    }

    public int minimumSize2(int[] nums, int maxOperations) {
        int left = 1, right = Arrays.stream(nums).max().getAsInt();
        int ans = 0;
        while (left <= right) {
            int mid = (left + right) / 2;
            long ops = 0;
            for (int x : nums) {
                ops += (x - 1) / mid;
            }
            if (ops <= maxOperations) {
                ans = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }

}
